package com.net.protocol;

import com.net.protocol.exception.ConnectException;
import com.net.protocol.util.Logger;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * TCP连接类，方法未加同步，不支持多线程
 *
 */
public class TCPConnector extends RestartableThread {

    /**
     * 连接状态监听
     */
    public interface ConnectListener {

        /**
         * 连接建立
         */
        public void onConnected();

        /**
         * 连接断开
         */
        public void onDisconnected();

    }

    private static final String TAG = "{TCPConnector}";

    private static final int SOCKET_TIME_OUT = 1000 * 5; // socket超时时间
    private static final int SOCKET_RECONNECT_DELAY_TIME = 3 * 1000; // 连接失败后重新连接的推迟时间

    private ConnectListener connectListener; // 连接状态改变的监听

    private String host; // 主机地址
    private int port; // 端口号
    private Socket socket;

    private volatile boolean running; //运行状态
    private volatile boolean connected; // 连接状态
    private long lastCommTime; //最后通信时间

    /**
     * 构造方法
     *
     * @param listener   连接状态监听
     * @param threadName 线程名
     */
    public TCPConnector(ConnectListener listener, String threadName) {
        this.connectListener = listener;
        super.setName(threadName);
    }

    /**
     * 构造方法
     *
     * @param host       主机
     * @param port       端口号
     * @param listener   连接状态监听
     * @param threadName 线程名
     */
    public TCPConnector(String host, int port, ConnectListener listener, String threadName) {
        this.host = host;
        this.port = port;
        this.connectListener = listener;
        super.setName(threadName);
    }

    public TCPConnector configHost(String host) {
        this.host = host;
        return this;
    }

    public TCPConnector configPort(int port) {
        this.port = port;
        return this;
    }

    /**
     * 获取连接状态
     */
    public boolean isConnected() {
        return running && connected;
    }

    /**
     * 获取最后通信时间
     */
    public long getLastCommTime() {
        return lastCommTime;
    }

    @Override
    public void execute() {
        while (running && !connected) { // 未连接成功，则休眠3后后继续重试
            try {
                Logger.i(TAG, "===线程{" + Thread.currentThread().getName() + "}===1.1连接服务器" + host + ":" + port);
                InetSocketAddress isa = new InetSocketAddress(host, port);
                socket = new Socket(); // 连接通道
                socket.connect(isa, SOCKET_TIME_OUT); // 连接服务器
                if (socket.isConnected()) {
                    connected = true;
                    if (connectListener != null) {
                        Logger.i(TAG, "===线程{" + Thread.currentThread().getName() + "}===连接成功");
                        connectListener.onConnected(); // 连接成功通知
                    }
                    continue;
                }
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                Logger.e(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "1.2建立TCP连接失败，3s后重试");
                Thread.sleep(SOCKET_RECONNECT_DELAY_TIME); // 休眠3s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 停止运行
     */
    public void stopRunning() {
        running = false;
        connected = false;
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 开始运行
     */
    public void startRunning() {
        running = true;
        restart();
    }

    /**
     * 重连
     */
    private void reconnect() {
        if (!running) {
            return; //如果主动停止该任务运行，则不需要重连，直接返回
        }
        connected = false; // 设置连接状态为断开
        if (connectListener != null) {
            Logger.i(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "4.1通知客户端连接断开");
            connectListener.onDisconnected(); // 连接断开通知
        }
        restart(); // 重启线程
        Logger.i(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "4.2重新建立TCP连接");
    }

    /**
     * 写数据
     *
     * @param sendBuf 要写入的缓冲
     * @param offset  偏移量
     * @param length  长度
     * @throws ConnectException
     */
    public void write(byte[] sendBuf, int offset, int length) throws ConnectException {
        if (!connected) { // 如果连接断开，直接返回
            Logger.e(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "2.1连接已断开");
            throw new ConnectException("连接已断开");
        }
        try {
            socket.getOutputStream().write(sendBuf, offset, length);
            lastCommTime = System.currentTimeMillis();
        } catch (IOException e) {
            Logger.e(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "2.2TCP通道写数据异常,重新建立连接");
            e.printStackTrace();
            reconnect(); // 异常后重新建立连接
        }
    }

    /**
     * 读数据
     *
     * @param receiveBuf 数据要读入的数组
     * @param offset     偏移量
     * @throws ConnectException
     */
    public int read(byte[] receiveBuf, int offset) throws ConnectException {
        if (!connected) { // 如果连接断开，直接返回
            Logger.e(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "3.1连接已断开");
            throw new ConnectException("连接已断开");
        }
        try {
            int length = socket.getInputStream().read(receiveBuf, offset, receiveBuf.length - offset);
            lastCommTime = System.currentTimeMillis();
            return length;
        } catch (IOException e) {
            Logger.e(TAG, "===线程{" + Thread.currentThread().getName() + "}===" + "3.2TCP通道读数据异常,重新建立连接");
            e.printStackTrace();
            reconnect(); // 异常后重新建立连接
        }
        throw new ConnectException("连接已断开");
    }

}
